/*
 * Copyright Aristool AG Switzerland
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); 
 * you may not use this file except in compliance with the License. 
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0 
 * 
 * Unless required by applicable law or agreed to in writing, software 
 * distributed under the License is distributed on an "AS IS" BASIS, 
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 * See the License for the specific language governing permissions and 
 * limitations under the License.
 */
package org.opprua.tidy.beans;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.TreeMap;

import org.opprua.tidy.conversion.ChangerBroker;

/**
 * TODO: add Java doc
 * 
 * Prepared for first commit: 15.07.2007
 * 
 * @author Lin Li
 */
abstract class BeanXMLPaserElement {

  private BeanXMLPaserElement parent;
  private String field;

  public abstract BeanXMLPaserElement start(String name);

  public abstract Object getData();

  public abstract void addValue(String name, Object data);

  public BeanXMLPaserElement getParent() {
    return parent;
  }

  public String getField() {
    return field;
  }

  public void setParent(BeanXMLPaserElement parent) {
    this.parent = parent;
  }

  public void setField(String field) {
    this.field = field;
  }

  public static BeanXMLPaserElement createElement(Class<?> c) {
    if (c == null) return new IgnoreElement();
    if (c.isArray()) return new ArrayElement(c.getComponentType());
    if (c.equals(Properties.class)) return new PropertiesElement();
    return new BeanElement(c);
  }

  private static class BeanElement extends BeanXMLPaserElement {

    private Class<?> type;
    private Map<String, BeanField> types;
    private Map<String, Object> values;

    public BeanElement(Class<?> type) {
      this.type = type;
      types = BeanField.getFields(type);
      values = new TreeMap<String, Object>();
    }

    public BeanXMLPaserElement start(String cname) {
      BeanXMLPaserElement child = null;
      if(cname != null) {
        child = createElement(types.get(cname).getType());
      } else {
        child = createElement(type);
        type = null;
      }

      child.setParent(this);
      child.setField(cname);
      return child;
    }

    public Object getData() {
      if(type == null) {
        return values.get("");
      }
      Object res = BeanHandler.stuff(type);
      for (Iterator<String> iter = values.keySet().iterator(); iter.hasNext();) {
        String k = iter.next();

        BeanField g = types.get(k);
        if (g == null) continue;
        try {
          g.set(res, values.get(k));
        } catch (Throwable e) {}
      }
      return res;
    }

    public void addValue(String name, Object value) {
      if (name == null) name = "";
      values.put(name, value);
    }
  }

  private static class ArrayElement extends BeanXMLPaserElement {

    private List<Object> values;
    private Class<?> type;

    public ArrayElement(Class<?> type) {
      this.type = type;
      values = new ArrayList<Object>();
    }

    public BeanXMLPaserElement start(String cname) {
      BeanXMLPaserElement child = createElement(type);

      child.setParent(this);
      child.field = cname;
      return child;
    }

    public Object getData() {
      Object res = Array.newInstance(type, values.size());
      for (int i = 0; i < values.size(); i++) {
        Object r = ChangerBroker.convert(type, values.get(i));
        Array.set(res, i, r);
      }
      return res;
    }

    public void addValue(String name, Object value) {
      values.add(value);
    }
  }

  private static class IgnoreElement extends BeanXMLPaserElement {

    public IgnoreElement() {
    }

    public BeanXMLPaserElement start(String cname) {
      BeanXMLPaserElement child = createElement(null);
      child.setParent(this);
      child.field = cname;
      return child;
    }

    public Object getData() {
      return null;
    }

    public void addValue(String name, Object value) {
    }
  }

  private static class PropertiesElement extends BeanXMLPaserElement {

    Properties p = new Properties();

    public PropertiesElement() {
    }

    public BeanXMLPaserElement start(String cname) {
      BeanXMLPaserElement child = createElement(null);
      child.setParent(this);
      child.field = cname;
      return child;
    }

    public Object getData() {
      return p;
    }

    public void addValue(String name, Object value) {
      p.put(name, value);
    }
  }
}
